﻿using System;
using System.Collections;
using System.Data;
using System.Data.Odbc;
using System.Configuration;
using System.IO;
using log4net;
using System.Threading;
using System.Text;
using QualityRpt.Service;
using System.Diagnostics;
using QualityRpt.Utils;

namespace QualityRpt.Jobs
{
	/// <summary>
	/// 原始数据导入任务的实现类
	/// </summary>
	public class RawDataJob
	{
		public RawDataJob ()
		{
		}

		/// <summary>
		/// 用于装入数据的客户端工具连接符
		/// </summary>
		static OdbcConnectionStringBuilder odbcs = new OdbcConnectionStringBuilder (ConfigurationManager.ConnectionStrings ["CLIENTConn"].ConnectionString);
		/// <summary>
		/// 用于命令的connect to 字符串和reset字符串
		/// </summary>
		static string strconnect = string.Empty;
		static string strreset = string.Empty;
		static ILog logger = LogManager.GetLogger ("RawDataJob");
		static Hashtable logFullFiles = Hashtable.Synchronized (new Hashtable ());
		static Hashtable logFiles = Hashtable.Synchronized (new Hashtable ());

		/// <summary>
		/// 判断文件是否可用
		/// </summary>
		/// <returns><c>true</c>, if file was checked, <c>false</c> otherwise.</returns>
		/// <param name="fileName">File name.</param>
		private static bool CheckFile (FileInfo fi)
		{
			bool rt = false;
			try {
				using(FileStream fileStream = File.OpenRead(fi.FullName))
				{
					rt = true;
					fileStream.Close();
				}
			} catch (IOException ex) {
				rt = false;
				logger.Info (fi.Name + "文件正在复制，等待完成");
			}
			return rt;
		}

		/// <summary>
		/// 获得文件对应要导入的表名
		/// 找不到对应的表时返回孔
		/// </summary>
		/// <returns>The file table.</returns>
		/// <param name="fi">Fi.</param>
		private static string GetFileTable (FileInfo fi)
		{
			string rt = string.Empty;
			//
			if (fi.FullName.ToUpper ().Contains ("ENTRY_TRAN_ZH")) {
				rt = "DB2INST1.L04_ENTRY_TRAN_ZH";
			} else if (fi.FullName.ToUpper ().Contains ("ENTRY_SPECIAL_ZH")) {
				rt = "DB2INST1.L04_ENTRY_SPECIAL_ZH";
			} else if (fi.FullName.ToUpper ().Contains ("EXIT_TRAN_ZH")) {
				rt = "DB2INST1.L05_EXIT_TRAN_ZH";
			} else if (fi.FullName.ToUpper ().Contains ("EXIT_SPECIAL_ZH")) {
				rt = "DB2INST1.L05_EXIT_SPECIAL_ZH";
			}
			return rt;
		}

		private static void GetConnect ()
		{
			//客户端db2命令连接
			object database = "quality";
			object uid = "administrator";
			object pwd = "1";
			odbcs.TryGetValue ("database", out database);
			odbcs.TryGetValue ("uid", out uid);
			odbcs.TryGetValue ("pwd", out pwd);
			strconnect = "db2 connect to " + database.ToString () + " user " + uid.ToString () + " using " + pwd.ToString ();
			strreset = "db2 connect reset";
		}

		/// <summary>
		/// 获得导入用的日志的数据行 
		/// </summary>
		/// <returns>The log row.</returns>
		private static DataRow GetInitLogRow ()
		{
			//记录导入日志用于导入提示
			DataRow logRow = SysLogsService.GetSchema ().NewRow ();
			logRow ["LOG_TYPE"] = 0;//信息
			logRow ["LOG_TIME"] = DateTime.Now;
			logRow ["LOG_DESCRIPTION"] = string.Empty;
			logRow ["LOG_SOURCE"] = SysLogsService.LOGSource.RawDataImport;//
			logRow ["LOG_CATE"] = 4;//
			logRow ["LOG_USER"] = 0;//系统
			logRow ["DATA_STATUS"] = 0;
			return logRow;
		}

		/// <summary>
		/// 装入数据，返回执行时间
		/// </summary>
		private static long LoadData (string ixffile, string tableName, out string outstr)
		{
			outstr = string.Empty;
			logger.Info (ixffile + "正在装入数据库");
			sbOutData.Clear ();
			sbOutErr.Clear ();
			FileInfo fi = new FileInfo (ixffile);
			string strSql = "db2 LOAD CLIENT FROM " + ixffile + " OF IXF MESSAGES " + backDir + "\\\\" + fi.Name + ".msg" + " INSERT INTO " + tableName + " COPY NO INDEXING MODE INCREMENTAL";
			if (logFullFiles.ContainsKey (ixffile)) {//如果已经处理过本文件或者出现过事务日志已满的问题则改用import
				string cmtcount = ConfigurationManager.AppSettings ["CommitCount"];
				if (cmtcount == null || cmtcount == string.Empty) {
					cmtcount = "20000";
				}
				strSql = "db2 IMPORT FROM " + ixffile + " OF IXF COMMITCOUNT " + cmtcount + " MESSAGES " + backDir + "\\\\" + fi.Name + ".msg" + " INSERT_UPDATE INTO " + tableName;
			}
			DateTime loadStart = DateTime.Now;
			StringBuilder sbbat = new StringBuilder ();
			sbbat.AppendLine (strconnect);
			sbbat.AppendLine (strSql);
			sbbat.AppendLine (strreset);
			outstr = RunDb2Cmd (sbbat.ToString ());
			logger.Info (outstr);
			TimeSpan tsLoad = DateTime.Now - loadStart;
			return (long)tsLoad.TotalSeconds;
		}

		/// <summary>
		/// 任务运行
		/// </summary>
		public static void Run ()
		{
			//备份目录创建
			if (!Directory.Exists (backDir)) {
				Directory.CreateDirectory (backDir);
			}
			GetConnect ();//获得连接和释放用命令行
			//文件扫描
			FileInfo[] fis = new DirectoryInfo (scanDir).GetFiles ();
			if (fis.Length > 0) {
				logger.Info ("扫描到" + fis.Length + "个文件.");
				for (int i = 0; i < fis.Length; i++) {
					if (!fis [i].FullName.ToLower ().EndsWith (".msg")) {
						#region 文件有效性判断
						logger.Info (fis [i].FullName + "文件正在判断有效性,最多会等待1个小时.");
						bool fiComplete = false;
						int icount = 0;
						while (!fiComplete) {
							fiComplete = CheckFile (fis [i]);//判断文件是否可用
							Thread.Sleep (60000);//等一分钟判断一次
							icount++;
							if (icount > 60) {//等待一个小时该文件
								break;
							}
						}
						if (!fiComplete) {
							continue;
						}
						//
						logger.Info (fis [i].FullName + "文件有效,正在根据文件名进行进一步的有效性判断.");
						string ixffile = fis [i].FullName;
						string tableName = GetFileTable (fis [i]);
						#endregion
						if (tableName == string.Empty) {
							logger.Info (ixffile + "文件名中不包含可以识别为要导入的表名部分");
						} else {
							try {
								DataRow logRow = GetInitLogRow ();//获得初始化的日志数据行
								string strMsg = string.Empty;
								long tSecond = LoadData (ixffile, tableName, out strMsg);
								logRow ["LOG_DESCRIPTION"] = fis [i].Name + "|" + tableName + "|" + StringUtils.FormatHMS (tSecond);
								bool lerr = false;
								if (strMsg.Contains ("错误")) {//装入时发生错误
									logRow ["LOG_DESCRIPTION"] = ixffile + "|" + tableName + "|装入时发生错误,请查看msg文件";
									logRow ["LOG_TYPE"] = 2;//错误
									lerr = true;
								} else if (strMsg.Contains ("事务日志已满")) {
									logRow ["LOG_DESCRIPTION"] = ixffile + "|" + tableName + "|装入时发生错误,请查看msg文件";
									logRow ["LOG_TYPE"] = 2;//错误
									if (!logFullFiles.ContainsKey (ixffile)) {
										logFullFiles.Add (ixffile, 0);//出现
									}
									lerr = true;
								}
								int iLoadQ = DBMJob.LoadQuery (tableName);
								if (iLoadQ == 2) {
									logRow ["LOG_TYPE"] = 2;//错误
									lerr = true;
								}
								if (!lerr) {//没有错误的情况下
									//复制到back目录
									Thread.Sleep (100);
									if (File.Exists (backDir + "\\\\" + fis [i].Name)) {
										File.Delete (backDir + "\\\\" + fis [i].Name);
									}
									File.Move (ixffile, backDir + "\\\\" + fis [i].Name);
								}
								if (!logFiles.ContainsKey (ixffile)) {
									DataRow rtRow = SysLogsService.Save (logRow);
									logFiles.Add (ixffile, rtRow ["ID"]);

								} else {
									int logid = (int)logFiles [ixffile];
									logRow ["ID"] = logid;
									SysLogsService.Update (logRow);
								}
								if (!lerr) {//日志存储或者更新后，如果是无错误提交则清除哈希中的该文件
									logFiles.Remove (ixffile);
								}
							} catch (Exception ex) {
								logger.Error (ex);
							}
						}
					} else {
						fis [i].Delete ();//msg文件不处理直接删除 
					}
				}
			}
		}

		private static string RunDb2Cmd (string batsql)
		{
			sbOutData = new StringBuilder ();
			String batPath = Environment.CurrentDirectory + "\\" + DateTime.Now.Ticks + ".bat";
			File.WriteAllText (batPath, batsql);
			Process process = new Process ();
			process.StartInfo.FileName = "db2cmd.exe";
			process.StartInfo.Arguments = "/i " + batPath;
			process.StartInfo.UseShellExecute = false;          //不使用系统外壳程序启动  
			process.StartInfo.RedirectStandardInput = false;     //重定向输入（一定是true） 
			process.StartInfo.RedirectStandardOutput = true;    //重定向输出  
			process.StartInfo.RedirectStandardError = true;
			process.StartInfo.CreateNoWindow = false;            //不创建窗口 
			process.OutputDataReceived += new DataReceivedEventHandler (process_OutputDataReceived);
			process.ErrorDataReceived += new DataReceivedEventHandler (process_ErrorDataReceived);
			process.Start ();
			process.BeginOutputReadLine ();
			process.BeginErrorReadLine ();
			process.WaitForExit ();
			process.Close ();
			File.Delete (batPath);
			return sbOutData.ToString ();
		}

		static StringBuilder sbOutErr = new StringBuilder ();
		static StringBuilder sbOutData = new StringBuilder ();

		static void process_ErrorDataReceived (object sender, DataReceivedEventArgs e)
		{
			sbOutErr.AppendLine (e.Data);
		}

		static void process_OutputDataReceived (object sender, DataReceivedEventArgs e)
		{
			sbOutData.AppendLine (e.Data);
		}

		static string scanDir = ConfigurationManager.AppSettings ["ScanDir"];
		static string backDir = ConfigurationManager.AppSettings ["BackDir"];
	}
}

